﻿using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Security;
using System.Threading;
using Sail.Logging;
using Sail.Security;

namespace Sail
{
    /// <summary>
    /// Invoke扩展
    /// </summary>
    public static class InvokeExtensions
    {
        /// <summary>
        /// Invoke扩展方法
        /// </summary>
        /// <param name="events">公开枚举器</param>
        /// <param name="dispatch">委托</param>
        /// <param name="logger">日志记录者</param>
        public static void Invoke<TEvents>(this IEnumerable<TEvents> events, Action<TEvents> dispatch, ILogger logger)
        {
            foreach (var sink in events)
            {
                try
                {
                    dispatch(sink); //{2} thrown from {0} by {1}
                }
                catch (Exception ex)
                {
                    if (IsLogged(ex))
                    {
                        logger.Error(ex, "来自{0}，由{1}抛出的异常信息:{2}", typeof(TEvents).Name, sink.GetType().FullName, ex.GetType().Name);
                    }

                    if (IsFatal(ex))
                    {
                        throw;
                    }
                }
            }
        }

        /// <summary>
        /// 是否记录日志
        /// </summary>
        /// <param name="ex">异常对象</param>
        /// <returns>true or false</returns>
        private static bool IsLogged(Exception ex)
        {
            return ex is SailSecurityException || !IsFatal(ex);
        }

        /// <summary>
        /// 是否抛出异常
        /// </summary>
        /// <param name="ex">异常对象</param>
        /// <returns>true or false</returns>
        private static bool IsFatal(Exception ex)
        {
            return ex is SailSecurityException ||
                   ex is StackOverflowException ||
                   ex is AccessViolationException ||
                   ex is AppDomainUnloadedException ||
                   ex is ThreadAbortException ||
                   ex is SecurityException ||
                   ex is SEHException;
        }
    }
}